package ext;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import org.junit.*;


public class YuewenJavaTest {
    public static void main1(String[] args) throws Exception {
        Object obj = new Object();
        Thread a = new Thread(() -> {
            int start = 1;
            synchronized (obj) {
                String name = Thread.currentThread().getName();
                for (int i = 0; i < 5; i++) {
                    System.out.println(name + start);
                    start += 2;
                    obj.notifyAll();
                    try {
                        obj.wait();
                    } catch (Exception e) {

                    }
                }
                System.out.println(name + "finished");
                obj.notifyAll();
            }
        }, "a");
        Thread b = new Thread(() -> {
            int start = 2;
            synchronized (obj) {
                String name = Thread.currentThread().getName();
                for (int i = 0; i < 5; i++) {
                    System.out.println(name + start);
                    start += 2;
                    obj.notifyAll();
                    try {
                        obj.wait();
                    } catch (Exception e) {

                    }
                }
                System.out.println(name + "finished");
            }
        }, "b");

        a.start();
        Thread.sleep(30);
        b.start();
    }

    // map转为javabean
    public static <T> T mapToObject(Map<String, Object> map, Class<T> beanClass) throws Exception {
        ClassLoader classLoader = beanClass.getClassLoader();
        classLoader.loadClass(beanClass.getName());
        T bean = beanClass.getConstructor(null).newInstance();

        for (String key : map.keySet()) {
            Object val = map.get(key);
            Field field = beanClass.getDeclaredField(key);
            // Method method = beanClass.getDeclaredMethod(key, val.getClass());
            field.setAccessible(true);
            field.set(bean, val);
        }
        return bean;
    }

    public int maximumProduct(int[] nums) {
        int len = nums.length;
        Arrays.sort(nums);
        int max = Integer.MIN_VALUE, m3 = Integer.MIN_VALUE, m2 = Integer.MIN_VALUE, m1 = Integer.MIN_VALUE,
                n2 = Integer.MAX_VALUE, n1 = Integer.MAX_VALUE, t;

        for (int i = 0; i < len; i++) {
            if (nums[i] > m3) {
                m1 = m2;
                m2 = m3;
                m3 = nums[i];
            } else if (nums[i] > m2) {
                m1 = m2;
                m2 = nums[i];
            } else if (nums[i] > m1) {
                m1 = nums[i];
            }

            if (nums[i] < n2) {
                n1 = n2;
                n2 = nums[i];
            } else if (n1 > nums[i]) {
                n1 = nums[i];
            }
        }

        if ((t = n1 * n2 * m3) > max)
            max = t;
        if ((t = m1 * m2 * m3) > max)
            max = t;
        return max;
    }

    @Test
    public void test() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "zs");
        map.put("age", 11);
        try {
            User user = YuewenJavaTest.mapToObject(map, User.class);
            System.out.println(user);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Test
    public void test2() {
        int[] nums = new int[] { -1000, -1000, -1000 };
        System.out.println(maximumProduct(nums));
    }

    /*
     * 建立三个线程A、B、C，A线程打印10次字母A，
     * B线程打印10次字母B,C线程打印10次字母C，但是要求三个线程同时运行，并且实现交替打印，即按照ABCABCABC的顺序打印。
     */
    public static void printABC() {
        Lock lock = new ReentrantLock();
        Condition aCondition = lock.newCondition();
        Condition bCondition = lock.newCondition();
        Condition cCondition = lock.newCondition();
        Thread a = new Thread(() -> {
            String name = Thread.currentThread().getName();
            for (int i = 0; i < 10; i++) {
                try {
                    lock.lock();
                    System.out.println(name);
                    bCondition.signal();
                    aCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }, "A");
        Thread b = new Thread(() -> {
            String name = Thread.currentThread().getName();
            for (int i = 0; i < 10; i++) {
                try {
                    lock.lock();
                    bCondition.await();
                    System.out.println(name);
                    cCondition.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }, "B");
        Thread c = new Thread(() -> {
            String name = Thread.currentThread().getName();
            for (int i = 0; i < 10; i++) {
                try {
                    lock.lock();
                    cCondition.await();
                    System.out.println(name);
                    aCondition.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }, "C");
        a.start();
        b.start();
        c.start();
    }

    public static void main2(String[] args) {
        printABC();
    }

    /*
     * 根据产品策略某本书可以设置包月到期时间，需要计算指定时间到包月到期时间还有多少分钟,不足60S的不计入。
     */
    public long remianTime(Date end) {
        Date now = new Date();
        long cur = now.getTime();
        long sub = end.getTime() - cur;
        return sub / (60 * 1000);
    }

    /*
     * 
     * 起点APP上允许用户对作品进行评论，为了防止用户恶意评论，发表不当内容，需要对用户发布的内容进行过滤，请写程序过滤用户发
     * 布内容中带有的QQ号（6~10位数字组成） 允许对内容严格操作，如用户发表了 作者大大666666，为你点赞 ，经过过滤后也可以为作者大大，为你点赞
     * ，将666666过滤掉了。
     */
    public String filter(String src) {
        return src.replaceAll("\\d{6,10}", "");
    }

    @Test
    public void test3() {
        System.out.println(filter("作者大大666666，为你点赞"));
    }

    public int stringNum(String s) {
        char[] cs = s.toCharArray();
        int len = s.length();
        boolean[][] f = new boolean[len][len];
        int count = 0;
        for (int j = 0; j < len; j++) {
            for (int i = 0; i <= j; i++) {
                if (i == j) {
                    f[i][j] = true;
                } else if (i == j - 1) {
                    f[i][j] = cs[i] == cs[j];
                } else {
                    f[i][j] = f[i + 1][j - 1] && cs[i] == cs[j];
                }
                if (f[i][j] && i != j)
                    System.out.println(s.substring(i, j + 1));
                count += f[i][j] ? 1 : 0;
            }
        }
        return count;
    }

    @Test
    public void test4() {
        System.out.println(stringNum("aabaa"));
    }

    // 矩阵乘法
    public int[][] multify(int[][] a, int[][] b) {
        int am = a.length, an = a[0].length;
        int bm = b.length, bn = b[0].length;
        int[][] res = new int[am][bn];
        if (an != bm)
            throw new RuntimeException();
        int n = an, t;
        for (int i = 0; i < am; i++) {
            for (int j = 0; j < bn; j++) {
                t = 0;
                for (int k = 0; k < n; k++) {
                    t += a[i][k] * b[k][j];
                }
                res[i][j] = t;
            }
        }
        return res;
    }

    // 去除字符串中的重复字符，对于出现超过2次（包含2次）的字符，只保留第一个。
    // 例：输入abcbdde，输出abcde。
    public String distinctString(String s) {
        char[] cs = s.toCharArray();
        Map<Character, Integer> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < cs.length; i++) {
            map.put(cs[i], map.getOrDefault(cs[i], 0) + 1);
        }
        int t;
        for (int i = cs.length - 1; i >= 0; i--) {
            if ((t = map.get(cs[i])) > 1) {
                map.put(cs[i], t - 1);
            } else
                sb.insert(0, cs[i]);
        }
        return sb.toString();
    }

    @Test
    public void test5() {
        System.out.println(distinctString("abcbdde"));
    }

    // 给定一个整型数组，移除数组的某个元素使其剩下的元素乘积最大，如果数组出现相同的元素 ，请输出第一次出现的元素
    public int makeBiggest(int[] nums) {
        BigDecimal bd = new BigDecimal(1);
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            bd = bd.multiply(new BigDecimal(nums[i]));
        }
        BigDecimal max = new BigDecimal(Integer.MIN_VALUE), tmp;
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            if ((tmp = bd.divide(BigDecimal.valueOf(nums[i]))).compareTo(max) > 0) {
                res = i;
                max = tmp;
            }
        }
        return res;
    }

    @Test
    public void test6() {
        BigDecimal bd = BigDecimal.valueOf(4);
        System.out.println(bd.divide(BigDecimal.valueOf(2)));
        int[] nums = {1,2,3,4,5};
        System.out.println(makeBiggest(nums));
    }

    @Test
    public void test7() {
        BigDecimal a = new BigDecimal(101);
        BigDecimal b = new BigDecimal(111);

        // 使用compareTo方法比较
        // 注意：a、b均不能为null，否则会报空指针
        if (a.compareTo(b) == -1) {
            System.out.println("a小于b");
        }

        if (a.compareTo(b) == 0) {
            System.out.println("a等于b");
        }

        if (a.compareTo(b) == 1) {
            System.out.println("a大于b");
        }

        if (a.compareTo(b) > -1) {
            System.out.println("a大于等于b");
        }

        if (a.compareTo(b) < 1) {
            System.out.println("a小于等于b");
        }
    }


    /* 
        假设有N个用户，其中有些人是朋友，有些则不是。A和B是朋友，B和C是朋友，这样ABC就是一个朋友圈，请计算给定的朋友关系的朋友圈数。
        给定一个 N * N 的矩阵 M，表示用户之间的朋友关系。
        如果M[i][j] = 1，表示已知第 i 个和 j 个人互为朋友关系，否则为不知道。你必须输出所有用户中的已知的朋友圈总数。
    */
    public int numOfFriends(int[][] nums) {
        //并查集，最低序号为根节点
        int n = nums.length;
        UnionFindSet ufs = new UnionFindSet(n);
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (nums[i][j] == 1) {
                    ufs.union(i, j);
                }
            }
        }
        return ufs.size;
    }

    class UnionFindSet {
        int[] parent;
        int[] weight;
        int size;

        public UnionFindSet(int num) {
            parent = new int[num];
            weight = new int[num];
            for (int i = 0; i < num; i++) {
                parent[i] = i;
                weight[i] = 1;
            }
            this.size = num;
        }

        public int find(int n) {
            if (parent[n] == n) {
                return n;
            }
            int r = find(n);
            parent[n] = r; //路径压缩
            return r;
        }

        public int union(int n1, int n2) {
            int r1 = find(n1),
                r2 = find(n2);
            if (r1 == r2) return weight[n1];
            else {
                size--;
                if (r1 <= r2) {
                    parent[r2] = r1;
                    weight[r1] += weight[r2];
                    return weight[r1];
                } else {
                    parent[r1] = r2;
                    weight[r2] += weight[r1];
                    return weight[r2];
                }
            }
        }
    }

    @Test
    public void test8() {
        // int[][] nums = {new int[]{1,0,1},new int[]{1,0,1}};
    }

    public void quickSort(long[] nums, int lo, int hi) {
        Stack<Integer> stack = new Stack<>();
        stack.push(lo);
        stack.push(hi);
        while (!stack.isEmpty()) {
            hi = stack.pop();
            lo = stack.pop();
            int i = lo, j = hi;
            long pivot = nums[i];
            while (i < j) {
                while (i < j && nums[j] >= pivot) j--;
                while (i < j && nums[i] <= pivot) i++;
                swap(nums, i, j);
            }
            swap(nums, i, lo);
            if (lo < i - 1) {
                stack.push(lo);
                stack.push(i - 1);
            }
            if (hi > i + 1) {
                stack.push(i + 1);
                stack.push(hi);
            }
        }
    }

    public void swap(long[] nums, int i, int j) {
        long tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }


    static class MyQueue {
        int[] queue;
        int lo, hi, size, capacity;

        public MyQueue(int n) {
            this.lo = this.hi = this.size = 0;
            this.capacity = n;
            this.queue = new int[n];
        }

        public MyQueue() {
            this(10);
        }

        public void push(int val) {
            if (hi == capacity) {
                if (lo > 0) {
                    //感觉用循环数组比较好，hi比lo小就不会出队0~hi的内容
                    int idx = 0;
                    for (int i = lo; i < hi; i++) {
                        queue[idx++] = queue[i];
                    }
                    lo = 0;
                    hi = idx;
                } else {
                    //扩容
                    int[] newQueue = new int[capacity * 2];
                    System.arraycopy(queue, 0, newQueue, 0, capacity);
                    this.queue = newQueue;
                }
            }
            this.queue[hi++] = val;
        }

        public int pop() {
            if (lo == hi) return -1;
            else {
                return this.queue[lo++];
            }
        }

    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // int[] nums = Arrays.stream().mapToInt(Integer::parseInt).toArray();
        String[] ss = scanner.nextLine().split(",");
        YuewenJavaTest.MyQueue myQueue = new YuewenJavaTest.MyQueue();
        for (String s: ss) {
            if (s.startsWith("push")) {
                myQueue.push(Integer.parseInt(s.split(" ")[1]));
            } else {
                System.out.println(myQueue.pop());
            }
        }
        scanner.close();
    }

    /* 
        在和外部公司联调HTTP接口时，对方要求调用的接口需要计算token，给到的规则如下：
        1） 所有的参数值必须经过urlencode,编码为utf-8；
        2） 对编码后数据按照key值进行字典升序排序；
        3）将所有的参数按照排序的顺序拼接成字符串 ，格式如下: k1=v1&k2=v2&k3=v3;
        4)  将第三步的算出的值计算md5值，md5加密的值小写

        请你编写一段方法计算token值
    */
    public String getToken(Map<String, String> params) {
        List<String> ss = params.keySet().stream().sorted().map(
            k -> {
                String v = params.get(k);
                String kv = null;
                try {
                    kv = k + "=" + URLEncoder.encode(v, "utf-8");
                } catch (Exception e) {

                }
                return kv;
            }
        ).collect(Collectors.toList());
        String token = null;
        try {
            token = new String(MessageDigest.getInstance("md5").digest(String.join("&", ss).getBytes()));
        } catch (Exception e) {
        }
        return token;
    }

    @Test
    public void test10() {
        Map<String, String> map = new HashMap<>();
        map.put("name", "张三");
        map.put("age", "11");
        String token = getToken(map);
        System.out.println(token);
    }
}
